/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include <stdlib.h>

#include "nmq.h" 
/* typedef -----------------------------------------------------------*/
/* define ------------------------------------------------------------*/
/* macro --------------------------------------------------------------*/
/* variables ----------------------------------------------------------*/
/* function prototypes ------------------------------------------------*/
/*********************************************************************************************

  * @brief  RxMsgQueue_Init
  * @param  mq: message queue object
            listMessage：the pool pointer
            count: the count of member
  * @retval 
  * @remark 

  ********************************************************************************************/
void RxMsgQueue_Init(struct RxMessageQueue *mq, void **listMessage, size_t count)
{
	mq->listMessage = (struct RxMessage **)listMessage;
	mq->_countTotal = count;
}
/*********************************************************************************************

  * @brief  RxMsgQueue_Add
  * @param  mq: message queue object
            packet：the packet pointer
            length: the byte count of packet
            ismalloc：if the packet is alloced at other line
  * @retval >=0, the index of message in queue
            -1：the length is 0, operation is not allow
            -105: no buffer space to alloc
            -28：the queue is full
  * @remark 

  ********************************************************************************************/
int RxMsgQueue_Add(struct RxMessageQueue *mq, uint8_t *packet, size_t length, bool ismalloc)
{
    uint8_t i = 0;

    if(length == 0)
    {   return -1;  }       // EPERM

    /*  look for a idle space to fill message*/
    for(i=0; i <mq->_countTotal; i++)
    {
        if(mq->listMessage[i] == NULL)
        {
			/* alloc space to put message struct */
			mq->listMessage[i] = (struct RxMessage *)malloc(sizeof(struct RxMessage));
			if(mq->listMessage[i] == NULL)
			{	return -105;	}       // ENOBUFS
			memset(mq->listMessage[i], 0, sizeof(struct RxMessage));
			
			/* if ismalloc set to true, dont need alloc new space to put message content */
            if(ismalloc == false)
            {
                mq->listMessage[i]->packet.start = (uint8_t *)malloc(length + 1);
                
                if(mq->listMessage[i]->packet.start == NULL)
                {   
					free(mq->listMessage[i]);
					return -105 ;  // ENOBUFS
				}
				memset(mq->listMessage[i]->packet.start, 0, length + 1);
                memcpy(mq->listMessage[i]->packet.start, packet, length);
            }
            else
            {   mq->listMessage[i]->packet.start = packet; }
            
			/* fill message parameter */
            mq->listMessage[i]->packet.length = length;               
            mq->_countUsed += 1;                       

            return i;

        }
    }
	
    return -28;     // ENOSPC
}

/*********************************************************************************************

  * @brief  RxMsgQueue_Process
  * @param  mq: message queue object
  * @retval 
  * @remark put this function into main loop handle

  ********************************************************************************************/
void RxMsgQueue_Process(struct RxMessageQueue *mq)
{
    if(mq->_countUsed == 0)
    {   return; }
  
    for(int i=0; i<mq->_countTotal; i++)
    {
        if (mq->listMessage[i] != NULL)
        {
            if(mq->event.new != NULL)
            {   mq->event.new(mq->listMessage[i]);    }

            free(mq->listMessage[i]->packet.start);      
			free(mq->listMessage[i]);
            mq->listMessage[i] = NULL; 
			
            mq->_countUsed -= 1;
        }
    }
}
/*********************************************************************************************

  * @brief  RxMsgQueue_RegisterEventNew
  * @param  mq: message queue object
  *         new: the handler of event new msg
  * @retval 
  * @remark 

  ********************************************************************************************/
inline void RxMsgQueue_RegisterEventNew(struct RxMessageQueue *mq, void (*new)(struct RxMessage *msg))
{   mq->event.new = new;  }
/*********************************************************************************************

  * @brief  TxMsgQueue_Init
  * @param  mq: message queue object
            listMessage：the pool pointer
            count: the count of member
            transmit: the operation function of process
  * @retval 
  * @remark 

  ********************************************************************************************/
void TxMsgQueue_Init(struct TxMessageQueue *mq, void **listMessage, size_t count, int (*transmit)(struct TxMessage *msg))
{
	mq->listMessage = (struct TxMessage **)listMessage;
	mq->_countTotal = count;
    mq->ops.transmit = transmit;   
}
/*********************************************************************************************

  * @brief  TxMsgQueue_Process
  * @param  mq: message queue object
  * @retval 0: no error occur
  *         -1：the mq is empty, operation is not allow
  * @remark put this function into main loop handle

  ********************************************************************************************/
int TxMsgQueue_Process(struct TxMessageQueue *mq)
{
    int i = 0, j = 0, result = -1, clearMode = 0;
    
	/* no message to handle*/
    if(mq->_countUsed == 0)
    {   return -1; }
    
    /* transmit  */
    if((mq->_time + mq->parameter.interval) > NETick_Get())
    {   return result; }
    else
    {   mq->_time = NETick_Get();  }
	
	
	/* if isDisorder is not set, transmit the earliest message until clear */
	/* if isDisorder is set, loop transmit the message which in queue */
    if(mq->parameter.isDisorder == true)
	{
		while(1)
		{
			if(mq->listMessage[mq->_index] != NULL)
			{	break;	}
			mq->_index += 1;
			if(mq->_index >= mq->_countTotal)
			{	mq->_index = 0;	}
		}
		
	}
	else
	{
		/* find the earliest message */
		if(mq->listMessage[mq->_index] == NULL)
		{
			j = -1;
			for(int i=0; i<mq->_countTotal; i++)
			{
				if(mq->listMessage[i] != NULL)
				{
					if(j == -1)
					{	j = i;	}
					if(mq->listMessage[i]->time < mq->listMessage[i]->time)
					{	j = i;	}
				}
			}
			mq->_index = j;
		}
		
	}
	
	/* handle the specified message */
	if((mq->listMessage[mq->_index]->flag & TX_FLAG_SENDED) == 0 
		|| mq->listMessage[mq->_index]->flag & TX_FLAG_RT)
	{
		if(mq->ops.transmit != NULL)
		{	clearMode = mq->ops.transmit(mq->listMessage[i]);	}
		
		mq->listMessage[mq->_index]->flag |= TX_FLAG_SENDED; // 标记为已发送
		mq->listMessage[mq->_index]->countRetry++;       // 重发次数递增

		if (clearMode != 0)
		{	goto Clear;	}
	}

	/* free mode 
		1. manual clear => 1, 
		2. clear after transmit
		3. clear after retry too many times	*/
	if((mq->listMessage[mq->_index]->flag & TX_FLAG_MC) == 0)
	{
		if ((mq->listMessage[mq->_index]->flag & TX_FLAG_RT) == 0)
		{	clearMode = 2;	}
		else if (mq->listMessage[mq->_index]->countRetry > mq->parameter.countMaxRetry)
		{	clearMode = 3;	}
	}

Clear: 
	/* free mode 
		1. manual clear => 1, 
		2. clear after transmit
		3. clear after retry too many times	*/
	if(clearMode != 0)
	{
		if (mq->event.clear != NULL)
		{   mq->event.clear(mq->listMessage[mq->_index], clearMode);  }
		TxMsgQueue_Free(mq, mq->_index);
	}

	return 0;
	
}

/*********************************************************************************************

  * @brief  TxMsgQueue_Add
  * @param  mq: message queue object
            message: the message pointer
            length：the byte count of message
            flag：tx message flag combine
  * @return -28 the queue is full
			-105, no space to alloc
  * @remark 

  ********************************************************************************************/
int TxMsgQueue_Add(struct TxMessageQueue *mq, uint8_t *message, size_t length, uint8_t flag, uint32_t param)
{
    int i=0;
	
	/*  look for a idle space to fill message*/
    for(i=0; i<mq->_countTotal; i++)
    {
        if (mq->listMessage[i] == NULL)
        {
			/* alloc space to put message struct */
			mq->listMessage[i] = (struct TxMessage *)malloc(sizeof(struct TxMessage));
			if(mq->listMessage[i] == NULL)
			{	return -105;	}   // ENOBUFS
			memset(mq->listMessage[i], 0, sizeof(struct TxMessage));
			
			/* if TX_FLAG_IS_MALLOC flag is set, dont need alloc new space to put message content */
            if((flag & TX_FLAG_IS_MALLOC) == 0)
            {
                mq->listMessage[i]->packet.start = (uint8_t *)malloc(length + 1);
                
                if(mq->listMessage[i]->packet.start == NULL)
                {   
					free(mq->listMessage[i]);
					return -105 ;  // ENOBUFS
				}
				memset(mq->listMessage[i]->packet.start, 0, length + 1);
                memcpy(mq->listMessage[i]->packet.start, message, length);
            }
            else
            {   mq->listMessage[i]->packet.start = message; }
            
			/* fill message parameter */
            mq->listMessage[i]->packet.length = length;                
            mq->listMessage[i]->flag |= flag;            	
            mq->listMessage[i]->time = NETick_Get();					
            mq->listMessage[i]->parameter = param;
            mq->_countUsed += 1;                       

            return i;
        }
    }

    return -28;     // ENOSPC
}
/*********************************************************************************************

  * TxMsgQueue_SetDisorder(struct TxMessageQueue *mq, bool enable)
  * TxMsgQueue_SetRetry(struct TxMessageQueue *mq, int count)
  * TxMsgQueue_SetInterval(struct TxMessageQueue *mq, int ms)

  ********************************************************************************************/
void TxMsgQueue_SetDisorder(struct TxMessageQueue *mq, bool enable)
{   mq->parameter.isDisorder = enable;  }
void TxMsgQueue_SetRetry(struct TxMessageQueue *mq, int count)
{   mq->parameter.countMaxRetry = count;    }
void TxMsgQueue_SetInterval(struct TxMessageQueue *mq, int ms)
{   mq->parameter.interval = ms;    }
/*********************************************************************************************

  * @brief  TxMsgQueue_ReigsterEventClear
  * @param  mq: message queue object
  *         clear: the handler of event clear
  * @retval 
  * @remark 

  ********************************************************************************************/
inline void TxMsgQueue_ReigsterEventClear(struct TxMessageQueue *mq, int (*clear)(struct TxMessage *msg, uint32_t))
{   mq->event.clear = clear;    }

/*********************************************************************************************

  * @brief  TxMsgQueue_Free
  * @param  mq: message queue object
            index, the index of the message which will be free
  * @return 
  * @remark 

  ********************************************************************************************/
void TxMsgQueue_Free(struct TxMessageQueue *mq, int index)
{
	if(mq->listMessage[index] != NULL)
	{
		free(mq->listMessage[index]->packet.start);
		free(mq->listMessage[index]);
		mq->listMessage[index] = NULL;
		mq->_countUsed -= 1;
	}
}

/*********************************************************************************************

  * @brief  TxMsgQueue_FreeByFunc
  * @param  mq: message queue object
            func，the free function 
            *param: the param of clear function 
  * @return 
  * @remark 

  ********************************************************************************************/
void TxMsgQueue_FreeByFunc(struct TxMessageQueue *mq, bool (*func)(struct TxMessage *, void *), void *param)
{
    uint16_t i;

    for(i=0; i<mq->_countTotal; i++)
    {
        if(mq->listMessage[i] != NULL)
        {
            if(func(mq->listMessage[i], param) == true)
            {   TxMsgQueue_Free(mq, i);  }
        }
    }
}
/*********************************************************************************************

  * @brief  TxMsgQueue_FreeByParam
  * @param  mq: message queue object
            param，when the param of message is equal to param, free it
  * @return 
  * @remark 

  ********************************************************************************************/
void TxMsgQueue_FreeByParam(struct TxMessageQueue *mq, uint32_t param)
{
    uint16_t i;

    for(i=0; i<mq->_countTotal; i++)
    {
        if (mq->listMessage[i] != NULL)
        {
            if (mq->listMessage[i]->parameter == param)
            {   TxMsgQueue_Free(mq, i);   }
        }
    }
}


/*********************************************************************************************

  * @brief  TxMsgQueue_FreeAll
  * @param  mq: message queue object
  * @return 
  * @remark 

  ********************************************************************************************/
void TxMsgQueue_FreeAll(struct TxMessageQueue *mq)
{
    for(int i=0; i< mq->_countTotal; i++)
    {
        if (mq->listMessage[i] != NULL)
        {   TxMsgQueue_Free(mq, i);  }
    }
}

